package cc.rengu.redp.system.service.impl;

import cc.rengu.redp.common.domain.QueryRequest;
import cc.rengu.redp.common.utils.SortUtil;
import cc.rengu.redp.flowable.service.SysWorkFlowService;
import cc.rengu.redp.system.dao.RoleMapper;
import cc.rengu.redp.system.dao.RoleMenuMapper;
import cc.rengu.redp.system.domain.Menu;
import cc.rengu.redp.system.domain.Role;
import cc.rengu.redp.system.domain.RoleMenu;
import cc.rengu.redp.system.manager.UserManager;
import cc.rengu.redp.system.service.MenuService;
import cc.rengu.redp.system.service.RoleMenuServie;
import cc.rengu.redp.system.service.RoleService;
import cc.rengu.redp.system.service.UserRoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service("roleService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Autowired
    private RoleMenuMapper roleMenuMapper;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RoleMenuServie roleMenuService;
    @Autowired
    private MenuService menuService;
    @Autowired
    private UserManager userManager;
    @Autowired
    private SysWorkFlowService sysWorkFlowService;

    @Override
    public IPage<Role> findRoles(Role role, QueryRequest request) {
        try {
            LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();

            if (StringUtils.isNotBlank(role.getRoleName())) {
                queryWrapper.eq(Role::getRoleName, role.getRoleName());
            }
            if (StringUtils.isNotBlank(role.getCreateTimeFrom()) && StringUtils.isNotBlank(role.getCreateTimeTo())) {
                queryWrapper
                        .ge(Role::getCreateTime, role.getCreateTimeFrom())
                        .le(Role::getCreateTime, role.getCreateTimeTo());
            }
            Page<Role> page = new Page<>();
            SortUtil.handlePageSort(request, page, true);
            return this.page(page, queryWrapper);
        } catch (Exception e) {
            log.error("获取角色信息失败", e);
            return null;
        }
    }

    @Override
    public List<Role> findUserRole(String userName) {
        return baseMapper.findUserRole(userName);
    }

    @Override
    public Role findByName(String roleName) {
        return baseMapper.selectOne(new LambdaQueryWrapper<Role>().eq(Role::getRoleName, roleName));
    }

    @Override
    public Role findById(String id) {
        return baseMapper.selectById(Long.valueOf(id));
    }

    @Override
    public void createRole(Role role) {
        role.setCreateTime(new Date());
        role.setRoleName(role.getRoleName().trim());
        this.save(role);
        this.sysWorkFlowService.saveGroup(role.getRoleName());

        String[] menuIds = role.getMenuId().split(StringPool.COMMA);
        setRoleMenus(role, menuIds);
    }

    @Override
    public void deleteRoles(String[] roleIds) throws Exception {

        List<String> list = new ArrayList<>(Arrays.asList(roleIds));
        //超级管理员禁止删除
        list.remove(Role.ADMIN_ROLE_ID);

        if (list.size() <= 0) return;

        String[] newRoleIds = list.toArray(new String[list.size()]);

        //同步流程引擎
        for(String roleId : newRoleIds){
            Role role = findById(roleId);
            this.sysWorkFlowService.deleteGroup(role.getRoleName());
        }

        // 查找这些角色关联了那些用户
        List<String> userIds = this.userRoleService.findUserIdsByRoleId(newRoleIds);

        this.roleMenuService.deleteRoleMenusByRoleId(newRoleIds);
        this.userRoleService.deleteUserRolesByRoleId(newRoleIds);

        baseMapper.deleteBatchIds(list.stream().map(i -> Long.valueOf(i)).collect(Collectors.toList()));

        // 重新将这些用户的角色和权限缓存到 Redis中
        this.userManager.loadUserPermissionRoleRedisCache(userIds);
    }

    @Override
    public void updateRole(Role role) throws Exception {
        // 查找这些角色关联了那些用户
        String[] roleId = {String.valueOf(role.getRoleId())};
        List<String> userIds = this.userRoleService.findUserIdsByRoleId(roleId);

        role.setModifyTime(new Date());
        baseMapper.updateById(role);

        roleMenuMapper.delete(new LambdaQueryWrapper<RoleMenu>().eq(RoleMenu::getRoleId, role.getRoleId()));

        String[] menuIds = role.getMenuId().split(StringPool.COMMA);
        setRoleMenus(role, menuIds);

        // 重新将这些用户的角色和权限缓存到 Redis中
        this.userManager.loadUserPermissionRoleRedisCache(userIds);
    }

    @Override
    public List<Role> getAllRoles() {
        LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 隐藏超级管理员角色
        lambdaQueryWrapper.ne(Role::getRoleId, Role.ADMIN_ROLE_ID);
        List<Role> roles = this.getBaseMapper().selectList(lambdaQueryWrapper);
        return roles;
    }

    private void setRoleMenus(Role role, String[] menuIds) {
        List<String> selectedMenuIds = Arrays.asList(menuIds);
        List<RoleMenu> roleMenus = new ArrayList<>();
        selectedMenuIds.forEach(menuId -> {
            RoleMenu rm = new RoleMenu();
            rm.setMenuId(Long.valueOf(menuId));
            rm.setRoleId(role.getRoleId());
            roleMenus.add(rm);
        });

        QueryWrapper<Menu> lqw = new QueryWrapper<>();
        lqw.in("menu_id", selectedMenuIds).select("DISTINCT parent_id");
        List<Menu> parentMenus = menuService.list(lqw);
        parentMenus.forEach( menu -> {
            if (menu.getParentId().equals(0L)){
                return;
            }
            if (!selectedMenuIds.contains(menu.getParentId())){
                RoleMenu rm = new RoleMenu();
                rm.setMenuId(Long.valueOf(menu.getParentId()));
                rm.setRoleId(role.getRoleId());
                roleMenus.add(rm);
            }
        });
        roleMenuService.saveBatch(roleMenus);
    }
}
